//
//  Observer.ts
//  PureMVC TypeScript Multicore
//
//  Copyright(c) 2024 Saad Shams <saad.shams@puremvc.org>
//  Your reuse is governed by the BSD-3-Clause License
//
/**
 * A base `Observer` implementation.
 *
 * An `Observer` is an object that encapsulates information
 * about an interested object with a method that should
 * be called when a particular `Notification` is broadcast.
 *
 * In PureMVC, the `Observer` class assumes these responsibilities:
 *
 * - Encapsulate the notification (callback) method of the interested object.
 * - Encapsulate the notification context (this) of the interested object.
 * - Provide methods for setting the notification method and context.
 * - Provide a method for notifying the interested object.
 *
 * @class Observer
 */
class Observer {
    /**
     * Constructor.
     *
     * The notification method on the interested object should take
     * one parameter of type `Notification`
     *
     * @param {((notification: INotification) => void) | null} notify - The method to be called when a notification is received. Can be `null`.
     * @param {any | null} context - The context in which to call the `notifyMethod`. Can be `null`.
     */
    constructor(notify, context) {
        this._notifyMethod = notify;
        this._notifyContext = context;
    }
    /**
     * Get the notification method.
     *
     * @returns {((notification: INotification) => void) | null} The current method or `null` if no method is set.
     */
    get notifyMethod() {
        return this._notifyMethod;
    }
    /**
     * Set the notification method.
     *
     * The notification method should take one parameter of type `Notification`.
     *
     * @param {((notification: INotification) => void) | null} value - The method to set for handling notifications. Can be `null`.
     */
    set notifyMethod(value) {
        this._notifyMethod = value;
    }
    /**
     * Get the notifyContext
     *
     * @returns {any | null} The current context or `null` if no context is set.
     */
    get notifyContext() {
        return this._notifyContext;
    }
    /**
     * Set the notification context.
     *
     * @param {any | null} value - The context to set. Can be `null`.
     */
    set notifyContext(value) {
        this._notifyContext = value;
    }
    /**
     * Notify the interested object.
     *
     * @param {INotification} notification - The notification to send to the observer.
     * @returns {void}
     */
    notifyObserver(notification) {
        var _a;
        (_a = this.notifyMethod) === null || _a === void 0 ? void 0 : _a.call(this.notifyContext, notification);
    }
    /**
     * Compare an object to the notification context.
     *
     * @param {any} object - The object to compare with the observer's context.
     * @returns {boolean} `true` if the context is the same, otherwise `false`.
     */
    compareNotifyContext(object) {
        return object == this.notifyContext;
    }
}

//
//  View.ts
//  PureMVC TypeScript Multicore
//
//  Copyright(c) 2024 Saad Shams <saad.shams@puremvc.org>
//  Your reuse is governed by the BSD-3-Clause License
//
/**
 * A Multiton `View` implementation.
 *
 * In PureMVC, the `View` class assumes these responsibilities:
 *
 * - Maintain a cache of `Mediator` instances.
 * - Provide methods for registering, retrieving, and removing `Mediators`.
 * - Notifying `Mediators` when they are registered or removed.
 * - Managing the observer lists for each `Notification` in the application.
 * - Providing a method for attaching `Observers` to a `Notification`'s observer list.
 * - Providing a method for broadcasting a `Notification`.
 * - Notifying the `Observers` of a given `Notification` when it broadcast.
 *
 * @see {@link Mediator}
 * @see {@link Observer}
 * @see {@link Notification}
 *
 * @class View
 */
class View {
    /**
     * Constructor.
     *
     * This `View` implementation is a Multiton,
     * so you should not call the constructor
     * directly, but instead call the static Multiton
     * Factory method `View.getInstance(multitonKey)`
     *
     * @param {string} key
     *
     * @throws {Error} Error if instance for this Multiton key has already been constructed
     */
    constructor(key) {
        if (View.instanceMap[key] != null)
            throw Error(View.MULTITON_MSG);
        this.multitonKey = key;
        View.instanceMap[key] = this;
        this.mediatorMap = {};
        this.observerMap = {};
        this.initializeView();
    }
    /**
     * Initialize the Multiton View instance.
     *
     * Called automatically by the constructor, this
     * is your opportunity to initialize the Multiton
     * instance in your subclass without overriding the
     * constructor.
     *
     * @returns {void}
     */
    initializeView() {
    }
    /**
     * View Multiton factory method.
     *
     * @param {string} key - The key used to identify the view instance.
     * @param {(key: string) => IView} factory - A factory function that creates a new instance of the view if one does not already exist for the specified key.
     * @returns {IView} The view instance associated with the given key.
     */
    static getInstance(key, factory) {
        if (View.instanceMap[key] == null)
            View.instanceMap[key] = factory(key);
        return View.instanceMap[key];
    }
    /**
     * Register an `Observer` to be notified
     * of `Notifications` with a given name.
     *
     * @param {string} notificationName - The name of the notification to which the observer should be registered.
     * @param {IObserver} observer - The observer instance to be registered.
     * @returns {void}
     */
    registerObserver(notificationName, observer) {
        if (this.observerMap[notificationName] != null) {
            this.observerMap[notificationName].push(observer);
        }
        else {
            this.observerMap[notificationName] = [observer];
        }
    }
    /**
     * Notify the `Observers` for a particular `Notification`.
     *
     * All previously attached `Observers` for this `Notification`'s
     * list are notified and are passed a reference to the `Notification` in
     * the order in which they were registered.
     *
     * @param {INotification} notification - The notification containing the data or command details to be sent to observers.
     * @returns {void}
     */
    notifyObservers(notification) {
        if (this.observerMap[notification.name] == null)
            return;
        // Get a reference to the observer list for this notification name
        // Copy observers from reference array to a working array,
        // since the reference array may change during the notification loop
        const observers = this.observerMap[notification.name].slice();
        // Notify Observers from the working array
        for (let i = 0; i < observers.length; i++) {
            observers[i].notifyObserver(notification);
        }
    }
    /**
     * Remove the observer for a given notifyContext from an observer list for a given Notification name.
     *
     * @param {string} notificationName - The name of the notification for which the observer should be removed.
     * @param {any} notifyContext - The context of the observer to be removed.
     * @returns {void}
     */
    removeObserver(notificationName, notifyContext) {
        // the observer list for the notification under inspection
        const observers = this.observerMap[notificationName];
        // find the observer for the notifyContext
        for (let i = 0; i < observers.length; i++) {
            if (observers[i].compareNotifyContext(notifyContext)) {
                // there can only be one Observer for a given notifyContext
                // in any given Observer list, so remove it and break
                observers.splice(i, 1);
                break;
            }
        }
        // Also, when a Notification's Observer list length falls to
        // zero, delete the notification key from the observer map
        if (observers.length == 0) {
            delete this.observerMap[notificationName];
        }
    }
    /**
     * Register a `Mediator` instance with the `View`.
     *
     * Registers the `Mediator` so that it can be retrieved by name,
     * and further interrogates the `Mediator` for its
     * `Notification` interests.
     *
     * If the `Mediator` returns any `Notification`
     * names to be notified about, an `Observer` is created encapsulating
     * the `Mediator` instance's `handleNotification` method
     * and registering it as an `Observer` for all `Notifications` the
     * `Mediator` is interested in.
     *
     * @param {IMediator} mediator - The mediator instance to be registered.
     * @returns {void}
     */
    registerMediator(mediator) {
        // do not allow re-registration (you must to removeMediator fist)
        if (this.mediatorMap[mediator.name] != null)
            return;
        mediator.initializeNotifier(this.multitonKey);
        // Register the Mediator for retrieval by name
        this.mediatorMap[mediator.name] = mediator;
        // Get Notification interests, if any.
        const interests = mediator.listNotificationInterests();
        // Register Mediator as an observer for each notification of interests
        if (interests.length > 0) {
            // Create Observer referencing this mediator's handleNotification method
            const observer = new Observer(mediator.handleNotification, mediator);
            // Register Mediator as Observer for its list of Notification interests
            for (let i = 0; i < interests.length; i++) {
                this.registerObserver(interests[i], observer);
            }
        }
        // alert the mediator that it has been registered
        mediator.onRegister();
    }
    /**
     * Retrieve a `Mediator` from the `View`.
     *
     * @param {string} mediatorName - The name of the mediator to retrieve.
     * @returns {IMediator | null} The mediator instance associated with the given name, or `null` if no such mediator exists.
     */
    retrieveMediator(mediatorName) {
        return this.mediatorMap[mediatorName] || null;
    }
    /**
     * Check if a Mediator is registered or not
     *
     * @param {string} mediatorName - The name of the mediator to check.
     * @returns {boolean} `true` if a mediator with the specified name is registered; otherwise, `false`.
     */
    hasMediator(mediatorName) {
        return this.mediatorMap[mediatorName] != null;
    }
    /**
     * Remove a `Mediator` from the `View`.
     *
     * @param {string} mediatorName - The name of the mediator to be removed.
     * @returns {IMediator | null} The removed mediator instance, or `null` if no mediator with the given name was found.
     */
    removeMediator(mediatorName) {
        // Retrieve the named mediator
        const mediator = this.mediatorMap[mediatorName];
        if (mediator == null)
            return null;
        // for every notification this mediator is interested in...
        const interests = mediator.listNotificationInterests();
        for (let i = 0; i < interests.length; i++) {
            // remove the observer linking the mediator
            // to the notification interest
            this.removeObserver(interests[i], mediator);
        }
        // remove the mediator from the map
        delete this.mediatorMap[mediatorName];
        // alert the mediator that it has been removed
        mediator.onRemove();
        return mediator;
    }
    /**
     * Remove an IView instance
     *
     * @param {string} key - multitonKey of IView instance to remove
     * @returns {void}
     */
    static removeView(key) {
        delete View.instanceMap[key];
    }
}
/** Message Constants
 * @type {string} */
View.MULTITON_MSG = "View instance for this Multiton key already constructed!";
/** Multiton Instances
 * @type {{ [key: string]: IView }} */
View.instanceMap = {};

//
//  Controller.ts
//  PureMVC TypeScript Multicore
//
//  Copyright(c) 2024 Saad Shams <saad.shams@puremvc.org>
//  Your reuse is governed by the BSD-3-Clause License
//
/**
 * A Multiton `Controller` implementation.
 *
 * In PureMVC, the `Controller` class follows the
 * 'Command and Controller' strategy, and assumes these
 * responsibilities:
 *
 *
 * - Remembering which `Command`s
 * are intended to handle which `Notifications`.
 * - Registering itself as an `Observer` with
 * the `View` for each `Notification`
 * that it has a `Command` mapping for.
 * - Creating a new instance of the proper `Command`
 * to handle a given `Notification` when notified by the `View`.
 * - Calling the `Command`'s `execute`
 * method, passing in the `Notification`.
 *
 *
 * Your application must register `Commands` with the
 * Controller.
 *
 * The simplest way is to subclass `Facade`,
 * and use its `initializeController` method to add your
 * registrations.
 *
 * @see {@link View}
 * @see {@link Observer}
 * @see {@link Notification}
 * @see {@link SimpleCommand}
 * @see {@link MacroCommand}
 *
 * @class Controller
 */
class Controller {
    /**
     * Constructor.
     *
     * This `Controller` implementation is a Multiton,
     * so you should not call the constructor
     * directly, but instead call the static Factory method,
     * passing the unique key for this instance
     * `Controller.getInstance(multitonKey)`
     *
     * @param {string} key
     *
     * @throws {Error} Error if instance for this Multiton key has already been constructed
     */
    constructor(key) {
        if (Controller.instanceMap[key] != null) {
            throw Error(Controller.MULTITON_MSG);
        }
        this.multitonKey = key;
        Controller.instanceMap[key] = this;
        this.commandMap = {};
        this.initializeController();
    }
    /**
     * Initialize the Multiton `Controller` instance.
     *
     * Called automatically by the constructor.
     *
     * Note that if you are using a subclass of `View`
     * in your application, you should <i>also</i> subclass `Controller`
     * and override the `initializeController` method in the
     * following way:
     *
     * ```ts
     * // ensure that the Controller is talking to my View implementation
     * initializeController() {
     *   this.view = MyView.getInstance("ViewTestKey1", (key: string) => new View(key));
     * }
     * ```
     * @returns {void}
     */
    initializeController() {
        this.view = View.getInstance(this.multitonKey, (key) => new View(key));
    }
    /**
     * `Controller` Multiton Factory method.
     *
     * @param {string} key - The key used to identify the controller instance.
     * @param {(key: string) => IController} factory - A factory function that creates a new instance of the controller if one does not already exist for the specified key.
     * @returns {IController} The controller instance associated with the given key.
     */
    static getInstance(key, factory) {
        if (Controller.instanceMap[key] == null)
            Controller.instanceMap[key] = factory(key);
        return Controller.instanceMap[key];
    }
    /**
     * Register a particular `Command` class as the handler
     * for a particular `Notification`.
     *
     * If an `Command` has already been registered to
     * handle `Notification`s with this name, it is no longer
     * used, the new `Command` is used instead.
     *
     * The Observer for the new Command is only created if this the
     * first time a Command has been registered for this Notification name.
     *
     * @param {string} notificationName - The name of the notification to associate with the command.
     * @param {() => ICommand} factory - A factory function that returns an instance of the command.
     * @returns {void}
     */
    registerCommand(notificationName, factory) {
        var _a;
        if (this.commandMap[notificationName] == null) {
            (_a = this.view) === null || _a === void 0 ? void 0 : _a.registerObserver(notificationName, new Observer(this.executeCommand, this));
        }
        this.commandMap[notificationName] = factory;
    }
    /**
     * If a `Command` has previously been registered
     * to handle the given `Notification`, then it is executed.
     *
     * @param {INotification} notification - The notification containing the data or command details needed for execution.
     * @returns {void}
     */
    executeCommand(notification) {
        const factory = this.commandMap[notification.name];
        if (factory == null)
            return;
        const command = factory();
        command.initializeNotifier(this.multitonKey);
        command.execute(notification);
    }
    /**
     * Check if a Command is registered for a given Notification
     *
     * @param {string} notificationName - The name of the notification to check for a registered command.
     * @returns {boolean} `true` if a command is registered for the specified notification name; otherwise, `false`.
     */
    hasCommand(notificationName) {
        return this.commandMap[notificationName] != null;
    }
    /**
     * Remove a previously registered `Command` to `Notification` mapping.
     *
     * @param {string} notificationName - The name of the notification for which the associated command should be removed.
     * @returns {void}
     */
    removeCommand(notificationName) {
        var _a;
        // if the Command is registered...
        if (this.hasCommand(notificationName)) {
            // remove the observer
            (_a = this.view) === null || _a === void 0 ? void 0 : _a.removeObserver(notificationName, this);
            // remove the command
            delete this.commandMap[notificationName];
        }
    }
    /**
     * Remove a Controller instance
     *
     * @param {string} key - The key used to identify the controller instance to be removed.
     * @returns {void}
     */
    static removeController(key) {
        delete Controller.instanceMap[key];
    }
}
/** Message Constants
 * @type {string} */
Controller.MULTITON_MSG = "Controller instance for this Multiton key already constructed!";
/** Multiton Instances
 * @type {{ [key: string]: IController }} */
Controller.instanceMap = {};

//
//  Model.ts
//  PureMVC TypeScript Multicore
//
//  Copyright(c) 2024 Saad Shams <saad.shams@puremvc.org>
//  Your reuse is governed by the BSD-3-Clause License
//
/**
 * A Multiton `Model` implementation.
 *
 * In PureMVC, the `Model` class provides
 * access to model objects (Proxies) by named lookup.
 *
 * The `Model` assumes these responsibilities:
 *
 * - Maintain a cache of `Proxy` instances.
 * - Provide methods for registering, retrieving, and removing
 * `Proxy` instances.
 *
 * Your application must register `Proxy` instances
 * with the `Model`. Typically, you use an
 * `Command` to create and register `Proxy`
 * instances once the `Facade` has initialized the Core
 * actors.
 *
 * @see {@link Proxy}
 *
 * @class Model
 */
class Model {
    /**
     * Constructor.
     *
     * This `Model` implementation is a Multiton,
     * so you should not call the constructor
     * directly, but instead call the static Multiton
     * Factory method `Model.getInstance(multitonKey)`
     *
     * @param {string} key
     *
     * @throws {Error} Error if instance for this Multiton key instance has already been constructed
     */
    constructor(key) {
        if (Model.instanceMap[key] != null)
            throw Error(Model.MULTITON_MSG);
        this.multitonKey = key;
        Model.instanceMap[key] = this;
        this.proxyMap = {};
        this.initializeModel();
    }
    /**
     * Initialize the `Model` instance.
     *
     * Called automatically by the constructor, this
     * is your opportunity to initialize the Multiton
     * instance in your subclass without overriding the
     * constructor.
     *
     * @returns {void}
     */
    initializeModel() {
    }
    /**
     * `Model` Multiton Factory method.
     *
     * @param {string} key - The key used to identify the model instance.
     * @param {(key: string) => IModel} factory - A factory function that creates a new instance of the model if one does not already exist for the specified key.
     * @returns {IModel} The model instance associated with the given key.
     */
    static getInstance(key, factory) {
        if (Model.instanceMap[key] == null)
            Model.instanceMap[key] = factory(key);
        return Model.instanceMap[key];
    }
    /**
     * Register a `Proxy` with the `Model`.
     *
     * @param {IProxy} proxy - The proxy instance to be registered.
     * @returns {void}
     */
    registerProxy(proxy) {
        proxy.initializeNotifier(this.multitonKey);
        this.proxyMap[proxy.name] = proxy;
        proxy.onRegister();
    }
    /**
     * Retrieve a `Proxy` from the `Model`.
     *
     * @param {string} proxyName - The name of the proxy to retrieve.
     * @returns {IProxy | null} The proxy instance associated with the given name, or `null` if no such proxy exists.
     */
    retrieveProxy(proxyName) {
        return this.proxyMap[proxyName] || null;
    }
    /**
     * Check if a Proxy is registered
     *
     * @param {string} proxyName - The name of the proxy to check.
     * @returns {boolean} `true` if a proxy with the specified name is registered; otherwise, `false`.
     */
    hasProxy(proxyName) {
        return this.proxyMap[proxyName] != null;
    }
    /**
     * Remove a `Proxy` from the `Model`.
     *
     * @param {string} proxyName - The name of the proxy to be removed.
     * @returns {IProxy | null} The removed proxy instance, or `null` if no proxy with the given name was found.
     */
    removeProxy(proxyName) {
        const proxy = this.proxyMap[proxyName];
        if (!proxy)
            return null;
        delete this.proxyMap[proxyName];
        proxy.onRemove();
        return proxy;
    }
    /**
     * Remove a Model instance
     *
     * @param {string} key - The key used to identify the model instance to be removed.
     * @returns {void}
     */
    static removeModel(key) {
        delete Model.instanceMap[key];
    }
}
/** Message Constants
 * @type {string} */
Model.MULTITON_MSG = "Model instance for this Multiton key already constructed!";
/** Multiton Instances
 * @type {{ [key: string]: IModel }} */
Model.instanceMap = {};

//
//  Notification.ts
//  PureMVC TypeScript Multicore
//
//  Copyright(c) 2024 Saad Shams <saad.shams@puremvc.org>
//  Your reuse is governed by the BSD-3-Clause License
//
/**
 * A base `Notification` implementation.
 *
 * PureMVC does not rely upon underlying event models such
 * as the one provided with Flash, and ActionScript 3 does
 * not have an inherent event model.
 *
 * The Observer Pattern as implemented within PureMVC exists
 * to support event-driven communication between the
 * application and the actors of the MVC triad.
 *
 * Notifications are not meant to be a replacement for Events
 * in Flex/Flash/Apollo. Generally, `Mediator` implementors
 * place event listeners on their view components, which they
 * then handle in the usual way. This may lead to the broadcast of `Notification`s to
 * trigger `Command`s or to communicate with other `Mediators`. `Proxy` and `Command`
 * instances communicate with each other and `Mediator`s
 * by broadcasting `Notification`s.
 *
 * A key difference between Flash `Event`s and PureMVC
 * `Notification`s is that `Event`s follow the
 * 'Chain of Responsibility' pattern, 'bubbling' up the display hierarchy
 * until some parent component handles the `Event`, while
 * PureMVC `Notification`s follow a 'Publish/Subscribe'
 * pattern. PureMVC classes need not be related to each other in a
 * parent/child relationship in order to communicate with one another
 * using `Notification`s.
 *
 * @class Notification
 */
class Notification {
    /**
     * Constructor.
     *
     * @param {string} name - The name of the notification.
     * @param {any} [body] - Optional data to be included with the notification.
     * @param {string} [type] - Optional type of the notification.
     */
    constructor(name, body, type) {
        this._name = name;
        this._body = body;
        this._type = type;
    }
    /**
     * Get the name of the `Notification` instance.
     *
     * @returns {string} The name of the notification.
     */
    get name() {
        return this._name;
    }
    /**
     * Get the body of the `Notification` instance.
     *
     * @returns {any} The body of the notification.
     */
    get body() {
        return this._body;
    }
    /**
     * Set the body of the `Notification` instance.
     *
     * @param {any} value - The new body to be set for the notification.
     */
    set body(value) {
        this._body = value;
    }
    /**
     * Get the type of the `Notification` instance.
     *
     * @returns {string | undefined} The type of the notification, or `undefined` if not set.
     */
    get type() {
        return this._type;
    }
    /**
     * Set the type of the `Notification` instance.
     *
     * @param {string | undefined} value - The new type to be set for the notification.
     */
    set type(value) {
        this._type = value;
    }
    /**
     * Get the string representation of the `Notification` instance.
     *
     * @returns {string} A string representation of the notification.
     */
    toString() {
        var _a;
        let msg = `Notification Name: ${this.name}`;
        msg += `\nBody: ${this.body ? this.body : "null"}`;
        msg += `\nType: ${(_a = this.type) !== null && _a !== void 0 ? _a : "null"}`;
        return msg;
    }
}

//
//  Facade.ts
//  PureMVC TypeScript Multicore
//
//  Copyright(c) 2024 Saad Shams <saad.shams@puremvc.org>
//  Your reuse is governed by the BSD-3-Clause License
//
/**
 * A base Multiton `Facade` implementation.
 *
 * @see {@link Model}
 * @see {@link View}
 * @see {@link Controller}
 *
 * @class Facade
 */
class Facade {
    /**
     * Constructor.
     *
     * This `Facade` implementation is a Multiton,
     * so you should not call the constructor
     * directly, but instead call the static Factory method,
     * passing the unique key for this instance
     * `Facade.getInstance(multitonKey)`
     *
     * @param {string} key
     *
     * @throws {Error} Error if instance for this Multiton key has already been constructed
     */
    constructor(key) {
        if (Facade.instanceMap[key] != null)
            throw Error(Facade.MULTITON_MSG);
        this.initializeNotifier(key);
        Facade.instanceMap[key] = this;
        this.initializeFacade();
    }
    /**
     * Initialize the Multiton `Facade` instance.
     *
     * Called automatically by the constructor. Override in your
     * subclass to do any subclass specific initializations. Be
     * sure to call `super.initializeFacade()`, though.
     *
     * @returns {void}
     */
    initializeFacade() {
        this.initializeModel();
        this.initializeController();
        this.initializeView();
    }
    /**
     * Facade Multiton Factory method
     *
     * @param {string} key - The unique key associated with the instance of the facade.
     * @param {(key: string) => IFacade} factory - A factory function that creates a new instance of the facade. It takes the key as a parameter and returns an `IFacade` instance.
     * @returns {IFacade} the Multiton instance of the Facade
     */
    static getInstance(key, factory) {
        if (Facade.instanceMap[key] == null)
            Facade.instanceMap[key] = factory(key);
        return Facade.instanceMap[key];
    }
    /**
     * Initialize the `Model`.
     *
     * Called by the `initializeFacade` method.
     * Override this method in your subclass of `Facade`
     * if one or both of the following are true:
     *
     * - You wish to initialize a different `Model`.
     * - You have `Proxy`s to register with the Model that do not
     * retrieve a reference to the Facade at construction time.`
     *
     * If you don't want to initialize a different `Model`,
     * call `super.initializeModel()` at the beginning of your
     * method, then register `Proxy`s.
     *
     * Note: This method is <i>rarely</i> overridden; in practice you are more
     * likely to use a `Command` to create and register `Proxy`s
     * with the `Model`, since `Proxy`s with mutable data will likely
     * need to send `Notification`s and thus will likely want to fetch a reference to
     * the `Facade` during their construction.
     *
     * @returns {void}
     */
    initializeModel() {
        this.model = Model.getInstance(this.multitonKey, (key) => new Model(key));
    }
    /**
     * Initialize the `Controller`.
     *
     * Called by the `initializeFacade` method.
     * Override this method in your subclass of `Facade`
     * if one or both of the following are true:
     *
     * - You wish to initialize a different `Controller`.
     * - You have `Commands` to register with the `Controller` at startup.`.
     *
     * If you don't want to initialize a different `Controller`,
     * call `super.initializeController()` at the beginning of your
     * method, then register `Command`s.
     *
     * @returns {void}
     */
    initializeController() {
        this.controller = Controller.getInstance(this.multitonKey, (key) => new Controller(key));
    }
    /**
     * Initialize the `View`.
     *
     * Called by the `initializeFacade` method.
     * Override this method in your subclass of `Facade`
     * if one or both of the following are true:
     *
     * - You wish to initialize a different `View`.
     * - You have `Observers` to register with the `View`
     *
     * If you don't want to initialize a different `View`,
     * call `super.initializeView()` at the beginning of your
     * method, then register `Mediator` instances.
     *
     * Note: This method is <i>rarely</i> overridden; in practice you are more
     * likely to use a `Command` to create and register `Mediator`s
     * with the `View`, since `Mediator` instances will need to send
     * `Notification`s and thus will likely want to fetch a reference
     * to the `Facade` during their construction.
     *
     * @returns {void}
     */
    initializeView() {
        this.view = View.getInstance(this.multitonKey, (key) => new View(key));
    }
    /**
     * Register a `Command` with the `Controller` by Notification name.     *
     *
     * @param {string} notificationName - The name of the notification to associate with the command.
     * @param {() => ICommand} factory - A factory function that returns an instance of ICommand. This function is used to create the command.
     * @returns {void}
     */
    registerCommand(notificationName, factory) {
        var _a;
        (_a = this.controller) === null || _a === void 0 ? void 0 : _a.registerCommand(notificationName, factory);
    }
    /**
     * Check if a Command is registered for a given Notification
     *
     * @param {string} notificationName - The name of the notification to check.
     * @returns {boolean} `true` if a command is registered for the notification; otherwise, `false`.
     */
    hasCommand(notificationName) {
        var _a, _b;
        return (_b = (_a = this.controller) === null || _a === void 0 ? void 0 : _a.hasCommand(notificationName)) !== null && _b !== void 0 ? _b : false;
    }
    /**
     * Remove a previously registered `Command` to `Notification` mapping from the Controller.
     *
     * @param {string} notificationName - The name of the notification for which the command should be removed.
     * @returns {void}
     */
    removeCommand(notificationName) {
        var _a;
        (_a = this.controller) === null || _a === void 0 ? void 0 : _a.removeCommand(notificationName);
    }
    /**
     * Register a `Proxy` with the `Model` by name.
     *
     * @param {IProxy} proxy - The proxy instance to be registered.
     * @returns {void}
     */
    registerProxy(proxy) {
        var _a;
        (_a = this.model) === null || _a === void 0 ? void 0 : _a.registerProxy(proxy);
    }
    /**
     * Retrieve a `Proxy` from the `Model` by name.
     *
     * @param {string} proxyName - The name of the proxy to retrieve.
     * @returns {IProxy | null} The proxy instance associated with the given name, or `null` if no such proxy exists.
     */
    retrieveProxy(proxyName) {
        var _a, _b;
        return (_b = (_a = this.model) === null || _a === void 0 ? void 0 : _a.retrieveProxy(proxyName)) !== null && _b !== void 0 ? _b : null;
    }
    /**
     * Check if a `Proxy` is registered
     *
     * @param {string} proxyName - The name of the proxy to check.
     * @returns {boolean} `true` if a proxy is registered for the name; otherwise, `false`.
     */
    hasProxy(proxyName) {
        var _a, _b;
        return (_b = (_a = this.model) === null || _a === void 0 ? void 0 : _a.hasProxy(proxyName)) !== null && _b !== void 0 ? _b : false;
    }
    /**
     * Remove a `Proxy` from the `Model` by name.
     *
     * @param {string} proxyName - The name of the proxy to remove.
     * @returns {IProxy | null} The removed proxy instance, or `null` if no such proxy exists.
     */
    removeProxy(proxyName) {
        var _a, _b;
        return (_b = (_a = this.model) === null || _a === void 0 ? void 0 : _a.removeProxy(proxyName)) !== null && _b !== void 0 ? _b : null;
    }
    /**
     * Register a `Mediator` with the `View`.
     *
     * @param {IMediator} mediator - The mediator instance to be registered.
     * @returns {void}
     */
    registerMediator(mediator) {
        var _a;
        (_a = this.view) === null || _a === void 0 ? void 0 : _a.registerMediator(mediator);
    }
    /**
     * Retrieve a `Proxy` from the `Model` by name.
     *
     * @param {string} mediatorName - The name of the mediator to retrieve.
     * @returns {IMediator | null} The mediator instance associated with the given name, or `null` if no such mediator exists.
     */
    retrieveMediator(mediatorName) {
        var _a, _b;
        return (_b = (_a = this.view) === null || _a === void 0 ? void 0 : _a.retrieveMediator(mediatorName)) !== null && _b !== void 0 ? _b : null;
    }
    /**
     * Check if a `Mediator` is registered or not
     *
     * @param {string} mediatorName - The name of the mediator to check.
     * @returns {boolean} `true` if a mediator is registered for the name; otherwise, `false`.
     */
    hasMediator(mediatorName) {
        var _a, _b;
        return (_b = (_a = this.view) === null || _a === void 0 ? void 0 : _a.hasMediator(mediatorName)) !== null && _b !== void 0 ? _b : false;
    }
    /**
     * Remove a `Mediator` from the `View`.
     *
     * @param {string} mediatorName - The name of the mediator to remove.
     * @returns {IMediator | null} The removed mediator instance, or `null` if no such mediator exists.
     */
    removeMediator(mediatorName) {
        var _a, _b;
        return (_b = (_a = this.view) === null || _a === void 0 ? void 0 : _a.removeMediator(mediatorName)) !== null && _b !== void 0 ? _b : null;
    }
    /**
     * Notify `Observer`s.
     *
     * This method is left public mostly for backward
     * compatibility, and to allow you to send custom
     * notification classes using the facade.
     *
     * Usually you should just call `sendNotification`
     * and pass the parameters, never having to
     * construct the notification yourself.
     *
     * @param {INotification} notification - The notification to be sent to observers.
     * @returns {void}
     */
    notifyObservers(notification) {
        var _a;
        (_a = this.view) === null || _a === void 0 ? void 0 : _a.notifyObservers(notification);
    }
    /**
     * Create and send an `Notification`.
     *
     * Keeps us from having to construct new notification
     * instances in our implementation code.
     *
     * @param {string} notificationName - The name of the notification to be sent.
     * @param {any} [body] - Optional data to be included with the notification.
     * @param {string} [type] - Optional type of the notification.
     * @returns {void}
     */
    sendNotification(notificationName, body, type) {
        this.notifyObservers(new Notification(notificationName, body, type));
    }
    /**
     * Set the Multiton key for this facade instance.
     *
     * Not called directly, but instead from the
     * constructor when getInstance is invoked.
     * It is necessary to be public in order to
     * implement Notifier.
     *
     * @param {string} key - The unique key to identify this instance of the notifier.
     * @returns {void}
     */
    initializeNotifier(key) {
        this.multitonKey = key;
    }
    /**
     * Check if a Core is registered or not
     *
     * @param {string} key - The unique key to check for an existing facade instance.
     * @returns {boolean} `true` if a facade instance exists for the key; otherwise, `false`.
     */
    static hasCore(key) {
        return this.instanceMap[key] != null;
    }
    /**
     * Remove a Core.
     *
     * Remove the Model, View, Controller and Facade
     * instances for the given key.
     *
     * @param {string} key - The unique key associated with the facade instance to be removed.
     * @returns {void}
     */
    static removeCore(key) {
        if (this.instanceMap[key] == null)
            return;
        Model.removeModel(key);
        View.removeView(key);
        Controller.removeController(key);
        delete this.instanceMap[key];
    }
}
/** Message Constants
 * @type {string} */
Facade.MULTITON_MSG = "Facade instance for this Multiton key already constructed!";
/** Multiton Instances
 * @type {{ [key: string]: IFacade }} */
Facade.instanceMap = {};

//
//  Notifier.ts
//  PureMVC TypeScript Multicore
//
//  Copyright(c) 2024 Saad Shams <saad.shams@puremvc.org>
//  Your reuse is governed by the BSD-3-Clause License
//
/**
 * A Base `Notifier` implementation.
 *
 * `MacroCommand, Command, Mediator` and `Proxy`
 * all have a need to send `Notifications`.
 *
 * The `Notifier` interface provides a common method called
 * `sendNotification` that relieves implementation code of
 * the necessity to actually construct `Notifications`.
 *
 * The `Notifier` class, which all the above-mentioned classes
 * extend, provides an initialized reference to the `Facade`
 * Multiton, which is required for the convenience method
 * for sending `Notifications`, but also eases implementation as these
 * classes have frequent `Facade` interactions and usually require
 * access to the facade anyway.
 *
 * NOTE: In the MultiCore version of the framework, there is one caveat to
 * notifiers, they cannot send notifications or reach the facade until they
 * have a valid multitonKey.
 *
 * The multitonKey is set:
 *   - on a Command when it is executed by the Controller
 *   - on a Mediator is registered with the View
 *   - on a Proxy is registered with the Model.
 *
 * @see {@link Proxy}
 * @see {@link Facade}
 * @see {@link Mediator}
 * @see {@link MacroCommand}
 * @see {@link SimpleCommand}
 *
 * @class Notifier
 */
class Notifier {
    /**
     * Initialize this Notifier instance.
     *
     * This is how a Notifier gets its multitonKey.
     * Calls to sendNotification or to access the
     * facade will fail until after this method
     * has been called.
     *
     * Mediators, Commands or Proxies may override
     * this method in order to send notifications
     * or access the Multiton Facade instance as
     * soon as possible. They CANNOT access the facade
     * in their constructors, since this method will not
     * yet have been called.
     *
     * @param {string} key the multitonKey for this Notifier to use
     * @returns {void}
     */
    initializeNotifier(key) {
        this.multitonKey = key;
    }
    /**
     * Create and send an `Notification`.
     *
     * Keeps us from having to construct new Notification
     * instances in our implementation code.
     *
     * @param {string} notificationName - The name of the notification to be sent.
     * @param {any} [body] - Optional data to be included with the notification.
     * @param {string} [type] - Optional type of the notification.
     * @returns {void}
     */
    sendNotification(notificationName, body, type) {
        this.facade.sendNotification(notificationName, body, type);
    }
    /**
     * Return the Multiton Facade instance
     *
     * @returns {IFacade} The facade instance.
     * @throws {Error} If the multiton key is not initialized.
     */
    get facade() {
        if (this.multitonKey == null)
            throw Error(Notifier.MULTITON_MSG);
        return Facade.getInstance(this.multitonKey, (key) => new Facade(key));
    }
}
/** Message Constants
 * @type {string} */
Notifier.MULTITON_MSG = "multitonKey for this Notifier not yet initialized!";

//
//  SimpleCommand.ts
//  PureMVC TypeScript Multicore
//
//  Copyright(c) 2024 Saad Shams <saad.shams@puremvc.org>
//  Your reuse is governed by the BSD-3-Clause License
//
/**
 * A base `Command` implementation.
 *
 * Your subclass should override the `execute`
 * method where your business logic will handle the `Notification`.
 *
 * @see {@link Controller}
 * @see {@link Notification}
 * @see {@link MacroCommand}
 *
 * @class SimpleCommand
 * @extends Notifier
 */
class SimpleCommand extends Notifier {
    /**
     * Fulfill the use-case initiated by the given `Notification`.
     *
     * In the Command Pattern, an application use-case typically
     * begins with some user action, which results in a `Notification` being broadcast, which
     * is handled by business logic in the `execute` method of an
     * `Command`.
     *
     * @param {INotification} notification - The notification containing the data or command details to be processed.
     * @returns {void}
     */
    execute(notification) {
    }
}

//
//  MacroCommand.ts
//  PureMVC TypeScript Multicore
//
//  Copyright(c) 2024 Saad Shams <saad.shams@puremvc.org>
//  Your reuse is governed by the BSD-3-Clause License
//
/**
 * A base `Command` implementation that executes other `Command`s.
 *
 * A `MacroCommand` maintains a list of
 * `Command` Class references called `SubCommands`.
 *
 * When `execute` is called, the `MacroCommand`
 * instantiates and calls `execute` on each of its `SubCommands` turn.
 * Each `SubCommand` will be passed a reference to the original
 * `Notification` that was passed to the `MacroCommand`'s
 * `execute` method.
 *
 * Unlike `SimpleCommand`, your subclass
 * should not override `execute`, but instead, should
 * override the `initializeMacroCommand` method,
 * calling `addSubCommand` once for each `SubCommand`
 * to be executed.
 *
 * @see {@link Controller}
 * @see {@link Notification}
 * @see {@link SimpleCommand}
 *
 * @class MacroCommand
 * @extends Notifier
 */
class MacroCommand extends SimpleCommand {
    /**
     * Constructor.
     *
     * You should not need to define a constructor,
     * instead, override the `initializeMacroCommand`
     * method.
     *
     * If your subclass does define a constructor, be
     * sure to call `super()`.
     *
     */
    constructor() {
        super();
        this.subCommands = [];
        this.initializeMacroCommand();
    }
    /**
     * Initialize the `MacroCommand`.
     *
     * In your subclass, override this method to
     * initialize the `MacroCommand`'s `SubCommand`
     * list with `Command` class references like
     * this:
     *
     * ```ts
     * // Initialize MyMacroCommand
     * initializeMacroCommand() {
     *   this.addSubCommand(() => new app.FirstCommand());
     *   this.addSubCommand(() => new app.SecondCommand());
     *   this.addSubCommand(() => new app.ThirdCommand());
     * }
     * ```
     *
     * Note that `SubCommand`s may be any `Command` implementor,
     * `MacroCommand`s or `SimpleCommands` are both acceptable.
     */
    initializeMacroCommand() {
    }
    /**
     * Add a `SubCommand`.
     *
     * The `SubCommands` will be called in First In/First Out (FIFO)
     * order.
     *
     * @param {() => ICommand} factory - A factory function that creates an instance of ICommand. This function will be used to generate the sub-command.
     * @returns {void}
     */
    addSubCommand(factory) {
        this.subCommands.push(factory);
    }
    /**
     * Execute this `MacroCommand`'s `SubCommands`.
     *
     * The `SubCommands` will be called in First In/First Out (FIFO)
     * order.
     *
     * @param {INotification} notification - The notification containing the data or command details to be processed.
     * @returns {void}
     */
    execute(notification) {
        while (this.subCommands.length > 0) {
            const factory = this.subCommands.shift();
            const command = factory === null || factory === void 0 ? void 0 : factory();
            if (command) {
                command.initializeNotifier(this.multitonKey);
                command.execute(notification);
            }
        }
    }
}

//
//  Mediator.ts
//  PureMVC TypeScript Multicore
//
//  Copyright(c) 2024 Saad Shams <saad.shams@puremvc.org>
//  Your reuse is governed by the BSD-3-Clause License
//
/**
 * A base `Mediator` implementation.
 *
 * @see {@link View}
 *
 * @class Mediator
 * @extends Notifier
 */
class Mediator extends Notifier {
    /**
     * Constructor.
     *
     * @param {string} [name] - Optional name for the mediator. Defaults to `Mediator.NAME`.
     * @param {any} [viewComponent] - Optional view component associated with the mediator.
     */
    constructor(name, viewComponent) {
        super();
        this._name = name || Mediator.NAME;
        this._viewComponent = viewComponent;
    }
    /**
     * Called by the View when the Mediator is registered
     *
     * @returns {void}
     */
    onRegister() {
    }
    /**
     * Called by the View when the Mediator is removed
     *
     * @returns {void}
     */
    onRemove() {
    }
    /**
     * List the `Notification` names this
     * `Mediator` is interested in being notified of.
     *
     * @returns {string[]} An array of notification names.
     */
    listNotificationInterests() {
        return [];
    }
    /**
     * Handle `Notification`s.
     *
     * Typically, this will be handled in a switch statement,
     * with one 'case' entry per `Notification`
     * the `Mediator` is interested in.
     *
     * @param {INotification} notification - The notification to handle.
     * @returns {void}
     */
    handleNotification(notification) {
    }
    /**
     * the mediator name
     *
     * @returns {string} The name of the mediator.
     */
    get name() {
        return this._name;
    }
    /**
     * Get the `Mediator`'s view component.
     *
     * Additionally, an implicit getter will usually
     * be defined in the subclass that casts the view
     * object to a type, like this:
     *
     * @returns {any} The view component.
     */
    get viewComponent() {
        return this._viewComponent;
    }
    /**
     * Set the `Mediator`'s view component.
     *
     * @param {any} value - The new view component.
     */
    set viewComponent(value) {
        this._viewComponent = value;
    }
}
/** The default name for the mediator.
 * @type {string} */
Mediator.NAME = "Mediator";

//
//  Proxy.ts
//  PureMVC TypeScript Multicore
//
//  Copyright(c) 2024 Saad Shams <saad.shams@puremvc.org>
//  Your reuse is governed by the BSD-3-Clause License
//
/**
 * A base `Proxy` implementation.
 *
 * In PureMVC, `Proxy` classes are used to manage parts of the
 * application's data model.
 *
 * A `Proxy` might simply manage a reference to a local data object,
 * in which case interacting with it might involve setting and
 * getting of its data in synchronous fashion.
 *
 * `Proxy` classes are also used to encapsulate the application's
 * interaction with remote services to save or retrieve data, in which case,
 * we adopt an asynchronous idiom; setting data (or calling a method) on the
 * `Proxy` and listening for a `Notification` to be sent
 * when the `Proxy` has retrieved the data from the service.
 *
 * @see {@link Model}
 *
 * @class Proxy
 * @extends Notifier
 */
class Proxy extends Notifier {
    /**
     * Constructor
     *
     * @param {string} [name] - The name of the proxy. Defaults to `Proxy.NAME` if not provided.
     * @param {any} [data] - The data associated with the proxy. Can be `null`.
     */
    constructor(name, data) {
        super();
        this._name = name || Proxy.NAME;
        this._data = data;
    }
    /**
     * Called by the Model when the Proxy is registered
     *
     * @returns {void}
     */
    onRegister() {
    }
    /**
     * Called by the Model when the Proxy is removed
     *
     * @returns {void}
     */
    onRemove() {
    }
    /**
     * Get the proxy name
     *
     * @returns {string} The name of the proxy.
     */
    get name() {
        return this._name;
    }
    /**
     * Get the data object
     *
     * @returns {any} The current data or `undefined` if no data is set.
     */
    get data() {
        return this._data;
    }
    /**
     * Set the data object
     *
     * @param {any} value - The data to set. Can be `null`.
     */
    set data(value) {
        this._data = value;
    }
}
/**
 * The default name for the Proxy.
 *
 * @type {string}
 */
Proxy.NAME = "Proxy";

export { Controller, Facade, MacroCommand, Mediator, Model, Notification, Notifier, Observer, Proxy, SimpleCommand, View };
